'use babel';
/* @flow */

/*
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the LICENSE file in
 * the root directory of this source tree.
 */

import type {NuclideUri} from '../../commons-node/nuclideUri';
import type {
  Definition,
  DefinitionQueryResult,
} from '../../nuclide-definition-service/lib/rpc-types';

import {getDeclaration} from './libclang';
import findWholeRangeOfSymbol from './findWholeRangeOfSymbol';
import invariant from 'assert';
import {wordAtPosition} from '../../commons-atom/range';
import {trackTiming} from '../../nuclide-analytics';
import {GRAMMAR_SET, IDENTIFIER_REGEXP} from './constants';

export default class DefinitionHelpers {

  @trackTiming('clang.get-definition')
  static async getDefinition(
    editor: TextEditor,
    position: atom$Point,
  ): Promise<?DefinitionQueryResult> {
    invariant(GRAMMAR_SET.has(editor.getGrammar().scopeName));

    const src = editor.getPath();
    if (src == null) {
      return null;
    }

    const contents = editor.getText();

    const wordMatch = wordAtPosition(editor, position, IDENTIFIER_REGEXP);
    if (wordMatch == null) {
      return null;
    }

    const {range} = wordMatch;

    const result = await getDeclaration(editor, position.row, position.column);
    if (result == null) {
      return null;
    }

    const wholeRange =
      findWholeRangeOfSymbol(editor, contents, range, result.spelling, result.extent);
    const definition: Definition = {
      path: result.file,
      position: result.point,
      range: result.extent,
      language: 'clang',
      // TODO: projectRoot
    };

    if (result.spelling != null) {
      definition.id = result.spelling;
      definition.name = result.spelling;
    }

    return {
      queryRange: wholeRange,
      definitions: [definition],
    };
  }

  @trackTiming('clang.get-definition-by-id')
  static async getDefinitionById(
    filePath: NuclideUri,
    id: string,
  ): Promise<?Definition> {
    // TODO:
    return null;
  }
}
